/**
 * @date 2006-7-17
 */

package org.nanhill.xmlro.gsp.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;

import org.nanhill.xmlro.gsp.GspException;
import org.nanhill.xmlro.gsp.GspVersion;
import org.nanhill.xmlro.gsp.packet.GspPacket;
import org.nanhill.xmlro.gsp.packet.LocateService;
import org.nanhill.xmlro.gsp.packet.LocateServiceRequestPacket;
import org.nanhill.xmlro.gsp.packet.RequestPacket;
import org.nanhill.xmlro.gsp.packet.ResponsePacket;
import org.nanhill.xmlro.gsp.packet.SetServicePacket;
import org.nanhill.xmlro.gsp.packet.SetServiceResponsePacket;

/**
 * @author alin [xalinx at gmail dot com]
 * @date 2006-7-17
 */
public class DefaultGspAgent extends AbstractGspAgent {
	public DefaultGspAgent(GspVersion ver, String slsModuleName, String host,
			int port) {
		this(slsModuleName, host, port);
		this.transport.setVersion(ver);
	}

	public DefaultGspAgent(String slsModuleName, String host, int port) {
		this(slsModuleName);
		this.slsHost = host;
		this.slsPort = port;
	}

	public DefaultGspAgent(GspVersion ver, String slsModuleName) {
		this(slsModuleName);
		this.transport.setVersion(ver);
	}

	public DefaultGspAgent(String slsModuleName) {
		this.slsModuleName = slsModuleName;
	}

	public DefaultGspAgent() {

	}

	private InputStream getInput() throws GspException, IOException {
		Socket socket = this.getServiceSocket();
		return socket.getInputStream();
	}

	private OutputStream getOutput() throws GspException, IOException {
		Socket socket = this.getServiceSocket();
		return socket.getOutputStream();
	}

	private Socket getServiceSocket() throws GspException, IOException {
		InputStream is = null;
		OutputStream os = null;
		if (null == this.serviceSocket || serviceSocket.isClosed()) {
			this.serviceSocket = new Socket(serviceHost, servicePort);
			is = serviceSocket.getInputStream();
			os = serviceSocket.getOutputStream();
			SetServicePacket setPacket = new SetServicePacket();
			setPacket.setVersion(this.serviceVersion);
			setPacket.setId(System.currentTimeMillis() + "");
			setPacket.setServiceName(this.serviceName);
			transport.send(setPacket, os);
			SetServiceResponsePacket setResp = (SetServiceResponsePacket) transport
					.receivePacket(is);
			if (0 != setResp.getCode()) {
				throw new GspException((String) setResp.getBody());
			}
		}
		return this.serviceSocket;
	}

	public void locate() throws GspException, IOException {
		SetServicePacket setPacket = new SetServicePacket();
		setPacket.setVersion(serviceVersion);
		setPacket.setId(System.currentTimeMillis() + "");
		setPacket.setServiceName(this.slsModuleName);
		Socket slsSocket;
		InputStream is;
		OutputStream os;
		try {
			slsSocket = new Socket(slsHost, slsPort);
			is = slsSocket.getInputStream();
			os = slsSocket.getOutputStream();
		} catch (UnknownHostException e) {
			throw new GspException(e);
		} catch (IOException e) {
			throw new GspException(e);
		}
		transport.send(setPacket, os);
		SetServiceResponsePacket setResp = (SetServiceResponsePacket) transport
				.receivePacket(is);
		if (0 != setResp.getCode()) {
			throw new GspException((String) setResp.getBody());
		}
		LocateService ss = new LocateService();
		ss.setSvc(serviceName);
		ss.setVer(serviceVersion);
		RequestPacket locateReq = new LocateServiceRequestPacket(ss);
		transport.send(locateReq, os);
		ResponsePacket locateResp = (ResponsePacket) transport
				.receivePacket(is);
		Map locateRs = (Map) locateResp.getBody();
		if (0 != locateResp.getCode() || !(Boolean) locateRs.get("res")) {
			throw new GspException((String) locateRs.get("errorMsg"));
		}
		Map serviceLocation = (Map) ((List) locateRs.get("services")).get(0);
		this.serviceHost = (String) serviceLocation.get("host");
		this.servicePort = (Integer) serviceLocation.get("port");
		try {
			slsSocket.close();
		} catch (IOException e) {
			throw new GspException(e);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nanhill.xmlro.gsp.GspAgent#doService(org.nanhill.xmlro.gsp.type.RequestGspPacket)
	 */
	public Object doService(String command, Object body) throws GspException,
			IOException {
		OutputStream os = this.getOutput();
		RequestPacket<Object> req = new RequestPacket<Object>(command, body);
		req.setId(System.currentTimeMillis() + "");
		try {
			transport.send(req, os);
		} catch (IOException e) {
			this.close();
			os = this.getOutput();
			transport.send(req, os);
		}
		InputStream is = this.getInput();
		ResponsePacket resp = (ResponsePacket) transport.receivePacket(is);
		if (resp.getCode() != 0) {
			throw new GspException((String) resp.getBody());
		}
		return resp.getBody();
	}

	/*
	 * TODO
	 * 
	 * @see org.nanhill.xmlro.gsp.GspAgent#doService(org.nanhill.xmlro.gsp.type.RequestGspPacket,
	 *      E)
	 */
	public <E> GspPacket<E> doService(String command, Object body,
			E wrapRespBody) {
		return null;
	}

	/*
	 * TODO
	 * 
	 * @see org.nanhill.xmlro.gsp.GspAgent#doService(org.nanhill.xmlro.gsp.type.RequestGspPacket,
	 *      java.lang.Class)
	 */
	public <E> GspPacket<E> doService(String command, Object body,
			Class<E> wrapRespBodyClz) {
		return null;
	}

	public void close() throws GspException, IOException {
		if (null != this.serviceSocket && !this.serviceSocket.isClosed()) {
			this.serviceSocket.close();
		}
	}

}
